#include "test_dispatcher.h"
#include <sea_statemachine>

// -------------------------------------------------------------------------------------------------

class DispatchedSm : public sea::StateMachine
{
public:

    DispatchedSm( ): StateMachine( STATEMACHINE_NAME ) { }

    ACT( DispatchedSm, doAction1 );
    ACT( DispatchedSm, doAction2 );
};

ACT_IMPL( DispatchedSm, doAction1 ) { SEA_UNUSED_PARAM( ); }
ACT_IMPL( DispatchedSm, doAction2 ) { SEA_UNUSED_PARAM( ); }

SM_DECLARE(
    dummy_source,                   // Body name to use when creating the state machine
    DispatchedSm user_dispatched    // State machine source code
    {
        Exceptions
        {
            Ev_Sys_Terminate        exit            nojump
        }
        One 1 20
        {
            Ev_Sys_OnEntry          doAction1       nojump
            Ev_Sys_Heartbeat        doAction2       nojump
            Ev_Sys_LastHeartbeat    doAction2       Two
        }
        Two 100 2
        {
            Ev_Sys_OnEntry          doAction1       nojump
            Ev_Sys_Heartbeat        doAction2       nojump
            Ev_Sys_LastHeartbeat    doAction2       Tree
        }
        Tree 10 2
        {
            Ev_Sys_OnEntry          doAction1       nojump
            Ev_Sys_Heartbeat        doAction1       nojump
            Ev_Sys_LastHeartbeat    exit            nojump
        }
    } );

// -------------------------------------------------------------------------------------------------

void test_state_machine_dispatcher( )
{
    // ONE USER DISPATCHED STATE MACHINE
    {
        DispatchedSm* sm = NEW DispatchedSm;

        if ( sm->create( 0, dummy_source ) != sea::eOk ){ /* Error */ }
        if ( sm->start( sea::StateMachine::RunDispatched ) != sea::eOk ){ /* Error */ }

        // Stick the manager->dispatch( ) in your event loop
        while( sm->dispatch( ) == sea::eOk ){ }

        // State machine finished
        sm->release_ref( );
    }

    sea::Engine::terminate( );

    // ONE USER DISPATCHED STATE MACHINE. USE OF MANAGER.
    {
        DispatchedSm* sm = NEW DispatchedSm;

        if ( sm->create( 0, dummy_source ) != sea::eOk ){ /* Error */ }
        if ( sm->start( sea::StateMachine::RunDispatched ) != sea::eOk ){ /* Error */ }

        // State machine is not running until dispatch( ) is called
        sea::Manager* manager = sm->dispatcher( );

        // No need for sm handle if manager's used, unless 
        // interaction with it is required
        sm->release_ref( );

        // Stick the manager->dispatch( ) in your event loop
        while( manager->dispatch( ) == sea::eOk ){ }

        // State machine finished
    }

    sea::Engine::terminate( );

    // MANY user dispatched state machines
    {
        vector< sea::StateMachine* > sm_pool;
        DispatchedSm* sm = NULL;
        int n = 3;

        for( int i = 0; i < n; i++ )
        {
            sm = NEW DispatchedSm;
            if ( sm->create( 0, dummy_source ) != sea::eOk ){ /* Error */ }
            sm_pool.insert( sm_pool.end( ), sm );
        }

        if ( sm->start( sm_pool, sea::StateMachine::RunDispatched ) != sea::eOk ){ /* Error */ }

        // Pick any instance to get common manager/dispatcher
        sea::Manager* manager = sm->dispatcher( );

        // No need for sm handles if the manager is used, unless 
        // interaction with them is required
        for( int i = 0; i < n; i++ )sm_pool[ i ]->release_ref( );

        // Stick the manager->dispatch( ) in your event loop
        while( manager->dispatch( ) == sea::eOk ){ }

        // State machine finished
    }

    sea::Engine::terminate( );

    // COMBINED user dispatched state machines
    {
        DispatchedSm* sm;
        
        sm = NEW DispatchedSm;
        if ( sm->create( 0, dummy_source ) != sea::eOk ){ /* Error */ }
        if ( sm->start( sea::StateMachine::RunDispatched ) != sea::eOk ){ /* Error */ }
        sea::Manager* manager = sm->dispatcher( );
        sm->release_ref( );

        sm = NEW DispatchedSm;
        if ( sm->create( 0, dummy_source ) != sea::eOk ){ /* Error */ }
        if ( manager->join( sm ) != sea::eOk ){ /* Error */ }
        sm->release_ref( );

        // Stick the manager->dispatch( ) in your event loop
        while( manager->dispatch( ) == sea::eOk ){ }

        // State machine finished
    }

    sea::Engine::terminate( );
}

// -------------------------------------------------------------------------------------------------


